package utils

import (
	"bytes"
	"crypto/aes"
	"crypto/cipher"
	"crypto/hmac"
	cRand "crypto/rand"
	"crypto/sha256"
	"crypto/sha512"
	"encoding/gob"
	"encoding/hex"
	"errors"
	"fmt"
	"math/big"
	"mcpl/rpc-server/pkg/dataformat"
	"net"
	"os"
	"strconv"
	"strings"
	"time"
	"unicode"

	"github.com/google/uuid"
	"github.com/gookit/goutil/strutil"
)

const (
	TimeISO8601Format = "2006-01-02T15:04:05Z"
)

func GetMicroSecond() int64 {
	return time.Now().UnixNano() / 1000
}

func GetSecond() int64 {
	return time.Now().Unix()
}

func MaxInt64(a int64, b int64) int64 {
	if a > b {
		return a
	}
	return b
}

func MinInt64(a int64, b int64) int64 {
	if a < b {
		return a
	}
	return b
}

const (
	MaxInt32Value = 2147483648.0
	MinInt32Value = -2147483649.0
)

func JSONNumberUnify(value interface{}) {
	if valueMap, ok := value.(map[string]interface{}); ok {
		for k, v := range valueMap {
			switch inval := v.(type) {
			case map[string]interface{}:
				JSONNumberUnify(v)
			case []interface{}:
				JSONNumberUnify(v)
			case float64:
				if inval > MaxInt32Value || inval < MinInt32Value {
					valueMap[k] = int64(inval)
				} else {
					valueMap[k] = int(inval)
				}

			}
		}
	} else if valueSlice, ok := value.([]interface{}); ok {
		for k, v := range valueSlice {
			switch inval := v.(type) {
			case map[string]interface{}:
				JSONNumberUnify(v)
			case []interface{}:
				JSONNumberUnify(v)
			case float64:
				if inval > MaxInt32Value || inval < MinInt32Value {
					valueSlice[k] = int64(inval)
				} else {
					valueSlice[k] = int(inval)
				}
			}
		}
	}
}

func VariableNumberUnify(value interface{}) interface{} {
	switch realval := value.(type) {
	case float64:
		if realval > MaxInt32Value || realval < MinInt32Value {
			return int64(realval)
		} else {
			return int32(realval)
		}
	default:
		return value
	}
}

func IntArray2Map(intarray []int, mapstore map[int]int) {
	for _, i := range intarray {
		mapstore[i] = 1
	}
}

func GetBehindSeconds(timePointSecond int64) int {
	return int(time.Now().Unix() - timePointSecond)
}

func Timestamp2Str(timestamp int64) string {
	return time.Unix(timestamp, 0).Format(time.DateTime)
}

func ArrayInt32ToInt(src []int32) []int {
	dest := make([]int, len(src))

	for i, v := range src {
		dest[i] = int(v)
	}

	return dest
}

func TimeWait(sleepMicrosecond int64) <-chan time.Time {
	return time.After(time.Microsecond * time.Duration(sleepMicrosecond))
}

// join 整形数组
func JoinInts(ints []int, sep string) string {
	strs := make([]string, 0)
	for _, i := range ints {
		strs = append(strs, strconv.Itoa(i))
	}
	return strings.Join(strs, sep)
}

type TimeInfo struct {
	Hour    int
	Minite  int
	Second  int
	Weekday int
}

func GetTimeInfo() *TimeInfo {
	timeInfo := &TimeInfo{}
	t := time.Now()
	timeInfo.Hour, timeInfo.Minite, timeInfo.Second = t.Clock()
	timeInfo.Weekday = (int)(t.Weekday())
	return timeInfo
}

func Normalize(value, min, max float64) float64 {
	if min >= max {
		return value
	}
	return (value - min) / (max - min)
}

func DeepCopy(dst, src interface{}) error {
	var buf bytes.Buffer
	if err := gob.NewEncoder(&buf).Encode(src); err != nil {
		return err
	}
	return gob.NewDecoder(bytes.NewBuffer(buf.Bytes())).Decode(dst)
}

func PathExists(path string) (bool, error) {
	_, err := os.Stat(path)
	if err == nil {
		return true, nil
	}
	if os.IsNotExist(err) {
		return false, nil
	}
	return false, err
}

func GetMd5String(s string) string {
	return strutil.MD5(s)
}

func GetSha256String(s string) string {
	h := sha256.New()
	h.Write([]byte(s))
	return hex.EncodeToString(h.Sum(nil))
}

func HmacSha256(key string, data string) []byte {
	mac := hmac.New(sha256.New, []byte(key))
	_, _ = mac.Write([]byte(data))
	return mac.Sum(nil)
}

func AesEncryptCBC(originByte []byte, salt string) (encrypted []byte) {
	// 分组秘钥
	// NewCipher该函数限制了输入k的长度必须为16, 24或者32
	keyByte := []byte(salt)
	block, _ := aes.NewCipher(keyByte)
	blockSize := block.BlockSize()                                  // 获取秘钥块的长度
	originByte = pkcs5Padding(originByte, blockSize)                // 补全码
	blockMode := cipher.NewCBCEncrypter(block, keyByte[:blockSize]) // 加密模式
	encrypted = make([]byte, len(originByte))                       // 创建数组
	blockMode.CryptBlocks(encrypted, originByte)                    // 加密
	return encrypted
}

func pkcs5Padding(ciphertext []byte, blockSize int) []byte {
	padding := blockSize - len(ciphertext)%blockSize
	return append(ciphertext, bytes.Repeat([]byte{byte(padding)}, padding)...)
}

func pkcs5UnPadding(origData []byte) []byte {
	length := len(origData)
	unPadding := int(origData[length-1])
	return origData[:(length - unPadding)]
}

func AesDecryptCBC(encrypted []byte, key []byte) (decrypted []byte) {
	block, _ := aes.NewCipher(key)                              // 分组秘钥
	blockSize := block.BlockSize()                              // 获取秘钥块的长度
	blockMode := cipher.NewCBCDecrypter(block, key[:blockSize]) // 加密模式
	decrypted = make([]byte, len(encrypted))                    // 创建数组
	blockMode.CryptBlocks(decrypted, encrypted)                 // 解密
	decrypted = pkcs5UnPadding(decrypted)                       // 去除补全码
	return decrypted
}

func GetSha512String(s string) string {
	h := sha512.New()
	h.Write([]byte(s))
	return hex.EncodeToString(h.Sum(nil))
}

// 批量替换字符
func StringsReplace(s string, oldArr []string, newArr []string, limitArr ...int) string {
	limit := -1
	if len(oldArr) == 0 || s == "" {
		return s
	}
	if len(limitArr) > 0 {
		limit = limitArr[0]
	}
	for index, old := range oldArr {
		newStr := ""
		if index < len(newArr) {
			newStr = newArr[index]
		}
		s = strings.Replace(s, old, newStr, limit)
	}
	return s
}

func RandInt(min, max int) int {
	randNum, _ := cRand.Int(cRand.Reader, new(big.Int).SetInt64(int64(max-min)))
	return int(randNum.Int64()) + min
}

// StrToTimeObj return time.Time coresponding to string in format of yyyy-mm-dd HH:ii:ss
func StrToTimeObj(value string) (time.Time, error) {
	t, err := time.Parse(time.DateTime, value)
	if err == nil {
		return t, nil
	}
	return time.Time{}, err
}

func EncryptUserPass(pass string) string {
	return GetMd5String(pass)
}

// 获取从现在到明天凌晨的秒数
func GetToTomorrowSecs() int {
	t := time.Now().AddDate(0, 0, 1)
	return int(time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, time.Local).Sub(t).Seconds())
}

const (
	AccessKeySalt = "PrJNPCzLnzZJ5URf"
)

func GenerateAccessKey() string {
	prefix := strings.ReplaceAll(uuid.NewString(), "-", "")[:16]
	return prefix + GetMd5String(prefix + AccessKeySalt)[:16]
}

func IP4toInt(iPv4Address net.IP) int64 {
	ipv4int := big.NewInt(0)
	ipv4int.SetBytes(iPv4Address.To4())
	return ipv4int.Int64()
}

func InetNtoA(ip int64) string {
	return fmt.Sprintf(
		"%d.%d.%d.%d",
		byte(ip>>24), byte(ip>>16), byte(ip>>8), byte(ip),
	)
}

func Cidr2IpList(cidr string) ([]string, error) {
	ip, ipNet, err := net.ParseCIDR(cidr)
	if err != nil {
		return nil, err
	}

	var ips []string
	for ip := ip.Mask(ipNet.Mask); ipNet.Contains(ip); inc(ip) {
		ips = append(ips, ip.String())
	}
	return ips[1 : len(ips)-1], nil
}

func inc(ip net.IP) {
	for j := len(ip) - 1; j >= 0; j-- {
		ip[j]++
		if ip[j] > 0 {
			break
		}
	}
}

func Timestamp2ISO8601(timestamp int64) string {
	return time.Unix(timestamp, 0).UTC().Format(TimeISO8601Format)
}

func TimeFormatForISO8601(t time.Time) string {
	return t.UTC().Format(TimeISO8601Format)
}

func CIDRContains(ip string, cidrList []string) (bool, error) {
	if len(ip) == 0 || len(cidrList) == 0 {
		return false, errors.New("empty params")
	}

	// 解析 IP 地址
	ipAddr := net.ParseIP(ip)
	if ipAddr == nil {
		return false, errors.New("invalid ip")
	}

	for _, cidr := range cidrList {
		if cidr == "0.0.0.0/0" && dataformat.IsIPv4(ip) {
			return true, nil
		}
		if cidr == "::/0" && dataformat.IsCIDRv6(ip) {
			return true, nil
		}
		if len(cidr) == 0 || !dataformat.IsCIDR(cidr) {
			continue
		}
		// 解析 CIDR 范围
		_, ipNet, err := net.ParseCIDR(cidr)
		if err != nil {
			continue
		}

		// 判断 IP 地址是否在 CIDR 范围内
		if ipNet.Contains(ipAddr) {
			return true, nil
		}
	}
	return false, nil
}

func IsIPInList(ip string, addrList []string) (bool, error) {
	if len(ip) == 0 || len(addrList) == 0 {
		return false, errors.New("empty params")
	}

	// 解析 IP 地址
	ipAddr := net.ParseIP(ip)
	if ipAddr == nil {
		return false, errors.New("invalid ip")
	}

	cidrList := make([]string, 0)
	for _, addr := range addrList {
		if len(addr) == 0 {
			continue
		}
		if dataformat.IsCIDR(addr) {
			cidrList = append(cidrList, addr)
			continue
		}
		if ip == addr {
			return true, nil
		}
	}
	if len(cidrList) > 0 {
		return CIDRContains(ip, cidrList)
	}
	return false, nil
}

func RuneLen(s string) int {
	return len([]rune(s))
}

func ISO8601OrRFCToTime(strTime string) (time.Time, error) {
	t, err := time.Parse(TimeISO8601Format, strTime)
	if err != nil {
		// 尝试解析时间字符串，检测是否符合格式
		t, err = time.Parse(time.RFC3339, strTime)
		if err != nil {
			return time.Time{}, err
		}
	}
	return t, nil
}

func UpperFirstUnicode(s string) string {
	if len(s) == 0 {
		return s
	}
	runes := []rune(s) // 转换为 rune 切片以处理多字节字符
	runes[0] = unicode.ToUpper(runes[0])
	return string(runes)
}
